﻿using DocumentFormat.OpenXml.Packaging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Utilities;

namespace OpenXMLProcess
{
    public class MyDocument
    {
        //Xml properties
        public WordprocessingDocument Doc { get; set; }
        public string FileName { get; set; }
        public string TempFileName { get; set; }

        public static XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

        //MyDocument properties 
        private XDocument XMLDocument;
        private XElement _element;
        public XElement Element { get { return _element; } set { _element = value; } }
        public ParagraphItem DocumentItem { get; set; }
        public ParagraphItem Title { get; set; }
        public ParagraphItem TableOfContent { get; set; }
        public List<ParagraphItem> AllParagraghs { get; set; }

        //Constructer
        public MyDocument()
        {
            DocumentItem = new ParagraphItem(null, null);
            Title = new ParagraphItem(null, null);
            TableOfContent = new ParagraphItem(null, null);
        }

        //xml method
        public void Close()
        {
            try
            {
                Doc.Close();
                FileName = "";
            }
            catch (Exception ex)
            {

                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
            }

        }
        public void Open(string filename)
        {
            try
            {
                FileName = filename;
                System.Diagnostics.Debug.WriteLine("Open Document " + filename);
                TempFileName = ConvertDocument(filename, "docx");
                System.Diagnostics.Debug.WriteLine("Temp path: " + TempFileName);
                Doc = WordprocessingDocument.Open(TempFileName, true);
                XMLDocument = Doc.MainDocumentPart.GetXDocument();
                _element = XMLDocument.Element(w + "document").Element(w + "body");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
            }

        }
        public void Save()
        {

            System.Diagnostics.Debug.WriteLine("Saving Document...");
            //Doc.MainDocumentPart.PutXDocument();
            Doc.MainDocumentPart.Document.Save();
            object m = System.Reflection.Missing.Value;
            object oldFileName = (object)TempFileName;
            object readOnly = (object)false;
            Microsoft.Office.Interop.Word.Application ac = null;
            try
            {
                // First, create a new Microsoft.Office.Interop.Word.ApplicationClass.
                ac = new Microsoft.Office.Interop.Word.Application();

                // Now we open the document.
                Microsoft.Office.Interop.Word.Document doc = ac.Documents.Open(ref oldFileName, ref m, ref readOnly,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                     ref m, ref m, ref m, ref m, ref m, ref m);

                // Cast these items to object.  The methods we're calling 
                // only take object types in their method parameters. 
                object newFileName = (object)FileName;
                FileInfo fi = new FileInfo(FileName);
                // We will be saving this file as HTML format. 
                object fileType = null;

                switch (fi.Extension.ToLower())
                {
                    case "docx":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocument;
                        break;
                    case "doc":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocument97;
                        break;
                    case "txt":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatUnicodeText;
                        break;
                    case "html":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatHTML;
                        break;
                    default:
                        break;
                }

                // Save the file. 
                doc.SaveAs(ref newFileName, ref fileType,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
            }
            finally
            {
                // Make sure we close the application class. 
                if (ac != null)
                    ac.Quit(ref readOnly, ref m, ref m);
            }
        }
        public void SaveAs(string path)
        {
            System.Diagnostics.Debug.WriteLine("Saving Document...");
            //Doc.MainDocumentPart.PutXDocument();
            Doc.MainDocumentPart.Document.Save();
            object m = System.Reflection.Missing.Value;
            object oldFileName = (object)TempFileName;
            object readOnly = (object)false;
            Microsoft.Office.Interop.Word.Application ac = null;
            try
            {
                ac = new Microsoft.Office.Interop.Word.Application();
                Microsoft.Office.Interop.Word.Document doc = ac.Documents.Open(ref oldFileName, ref m, ref readOnly,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                     ref m, ref m, ref m, ref m, ref m, ref m);
                object newFileName = (object)path;
                FileInfo fi = new FileInfo(path);
                object fileType = null;

                switch (fi.Extension.ToLower())
                {
                    case "docx":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocument;
                        break;
                    case "doc":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocument97;
                        break;
                    case "txt":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatUnicodeText;
                        break;
                    case "html":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatHTML;
                        break;
                    default:
                        break;
                }
                doc.SaveAs(ref newFileName, ref fileType,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
            }
            finally
            {
                if (ac != null)
                    ac.Quit(ref readOnly, ref m, ref m);
            }
        }

        public static string ConvertDocument(string fileName, string tempExtension)
        {
            object m = System.Reflection.Missing.Value;
            object oldFileName = (object)fileName;
            object readOnly = (object)false;
            string tempfile = null;
            object newFileName = "";
            Microsoft.Office.Interop.Word.Application ac = null;
            try
            {
                // First, create a new Microsoft.Office.Interop.Word.ApplicationClass.
                ac = new Microsoft.Office.Interop.Word.Application();

                // Now we open the document.
                Microsoft.Office.Interop.Word.Document doc = ac.Documents.Open(ref oldFileName, ref m, ref readOnly,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                     ref m, ref m, ref m, ref m, ref m, ref m);

                // Create a temp file to save the HTML file to. 
                tempfile = Utils.GetTempFile(tempExtension);
                System.Diagnostics.Debug.WriteLine("Gen temp path: " + tempfile);
                // Cast these items to object.  The methods we're calling 
                // only take object types in their method parameters. 
                newFileName = (object)tempfile;

                // We will be saving this file as HTML format. 
                object fileType = null;

                switch (tempExtension.ToLower())
                {
                    case "docx":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatXMLDocument;
                        break;
                    case "doc":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocument;
                        break;
                    case "txt":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatUnicodeText;
                        break;
                    case "html":
                        fileType = (object)Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatHTML;
                        break;
                    default:
                        break;
                }

                // Save the file. 
                doc.SaveAs(ref newFileName, ref fileType,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m,
                    ref m, ref m, ref m, ref m, ref m, ref m, ref m);
                doc.Close();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
            }
            if (ac != null)
                ac.Quit(ref readOnly, ref m, ref m);
            return newFileName.ToString();
        }

        //mydocument method
        public List<ParagraphItem> getAllParagraghs()
        {
            var temp = _element.Elements(w + "p").Select(p => new ParagraphItem(p, null));
            System.Diagnostics.Debug.WriteLine("All paragraphs in this document: " + temp.Count());
            temp = temp.Where(p => p.Text != "");
            System.Diagnostics.Debug.WriteLine("All paragraphs has text in this document: " + temp.Count());
            AllParagraghs = temp.ToList();
            return AllParagraghs;
        }
        public void ExtractInfo()
        {
            getAllParagraghs();
            Title = AllParagraghs.Where(t => t.Style == "Title").FirstOrDefault();
            if (Title != null)
                System.Diagnostics.Debug.WriteLine("Title: " + Title.Text);
            var temp = AllParagraghs.Where(t => t.Style.Contains("TOC")).Select(t => t).ToList();
            if (temp.Count > 0)
                TableOfContent = GetTableOfContent(temp);
            temp = AllParagraghs.Where(t => !t.Style.Contains("TOC")).Select(t => t).ToList();
            if (temp.Count > 0)
                DocumentHierarchy(temp);
        }
        public string GetText()
        {
            string temp = "";
            foreach (var p in AllParagraghs)
            {
                temp += p.Text + "\n";
            }
            return temp;
        }
        public string GetTextFormat()
        {
            string temp = "";
            foreach (var p in AllParagraghs)
            {
                temp += p.TextFormat + "\n";
            }
            return temp;
        }
        public string GetXML()
        {
            return XMLDocument.Document.ToString();
        }

        private ParagraphItem GetTableOfContent(List<ParagraphItem> toclist)
        {
            System.Diagnostics.Debug.WriteLine("TOC before Hierarchy: " + toclist.Count);
            ParagraphItem toc = new ParagraphItem(null, null);
            ParagraphItem parent = toc;
            ParagraphItem lastItem = toclist[0];
            int ci, li, pi;
            lastItem.Parent = toc;
            parent.Items.Add(toclist[0]);
            for (int i = 1; i < toclist.Count; i++)
            {
                ci = int.Parse(toclist[i].Style[3].ToString());
                li = int.Parse(lastItem.Style[3].ToString());
                if (ci == li)
                {
                    lastItem = toclist[i];
                    lastItem.Parent = parent;
                    parent.Items.Add(lastItem);
                }
                else if (ci > li)
                {
                    parent = lastItem;
                    lastItem = toclist[i];
                    lastItem.Parent = parent;
                    parent.Items.Add(lastItem);
                }
                else
                {
                    do
                    {
                        parent = parent.Parent;
                    } while (parent.Element != null && ci <= int.Parse(parent.Style[3].ToString()));
                    lastItem = toclist[i];
                    lastItem.Parent = parent;
                    parent.Items.Add(lastItem);
                }
            }
            System.Diagnostics.Debug.WriteLine("TOC after Hierarchy: " + toc.Items.Count);
            return toc;
        }
        private void DocumentHierarchy(List<ParagraphItem> templist)
        {
            DocumentItem.Items.Clear();
            System.Diagnostics.Debug.WriteLine("Paragraph before Hierarchy: " + templist.Count);
            ParagraphItem parent = DocumentItem;
            int ci, li, pi;
            var indexFirst = templist.FindIndex(p => p.Style.Contains("Heading"));
            indexFirst=indexFirst == -1 ? 0 : indexFirst;
            ParagraphItem lastItem = templist[indexFirst];
            lastItem.Parent = DocumentItem;
            parent.Items.Add(templist[indexFirst]);
            for (int i = indexFirst + 1; i < templist.Count; i++)
            {
                if (templist[i].Style.Contains("Heading"))
                {
                    ci = int.Parse(templist[i].Style[7].ToString());
                    li = int.Parse(lastItem.Style[7].ToString());
                    if (ci == li)
                    {
                        lastItem = templist[i];
                        lastItem.Parent = parent;
                        parent.Items.Add(lastItem);
                    }
                    else if (ci > li)
                    {
                        parent = lastItem;
                        lastItem = templist[i];
                        lastItem.Parent = parent;
                        parent.Items.Add(lastItem);
                    }
                    else
                    {
                        do
                        {
                            parent = parent.Parent;
                        } while (parent.Element != null && ci <= int.Parse(parent.Style[7].ToString()));
                        lastItem = templist[i];
                        lastItem.Parent = parent;
                        parent.Items.Add(lastItem);
                    }
                }
                else
                {

                    templist[i].Parent = lastItem;
                    lastItem.Items.Add(templist[i]);
                }
            }

            System.Diagnostics.Debug.WriteLine("Document Hierarchy completed,Sub Item in this document: " + DocumentItem.Items.Count);
        }
        private void ZipParagraph(List<ParagraphItem> para)
        {
            string temp;
            List<RunItem> texts;
            foreach (var p in para)
            {
                texts = p.GetRunsList();
                for (int i = 0; i < texts.Count() - 1; i++)
                {
                    if (texts[i].Format.ToString().Equals(texts[i + 1].Format.ToString()))
                    {
                        if (texts[i + 1].Element.Element(w + "tab") != null)
                            continue;
                        temp = texts[i].Text + texts[i + 1].Text;
                        texts[i].Text = temp;
                        texts[i + 1].Element.Remove();
                        i--;
                    }
                }

            }
        }

    }
    public static class LocalExtensions
    {
        public static XDocument GetXDocument(this OpenXmlPart part)
        {
            XDocument xdoc = part.Annotation<XDocument>();
            if (xdoc != null)
                return xdoc;
            using (StreamReader sr = new StreamReader(part.GetStream()))
            using (XmlReader xr = XmlReader.Create(sr))
                xdoc = XDocument.Load(xr);
            part.AddAnnotation(xdoc);
            return xdoc;
        }

        public static void PutXDocument(this OpenXmlPart part)
        {
            XDocument xdoc = part.GetXDocument();
            if (xdoc != null)
            {
                // Serialize the XDocument object back to the package.
                using (XmlWriter xw = XmlWriter.Create(part.GetStream(FileMode.Create, FileAccess.Write)))
                {
                    xdoc.Save(xw);
                }
            }
        }

        public static string StringConcatenate(this IEnumerable<string> source)
        {
            return source.Aggregate(
                new StringBuilder(),
                (s, i) => s.Append(i),
                s => s.ToString());
        }
    }
}
